using Flurl.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;
using System.IO;
using Senparc.CO2NET.HttpUtility;

namespace Pay.Core.WeiXin {
	/// <summary>
	/// 微信支付接口，官方API：https://mp.weixin.qq.com/paymch/readtemplate?t=mp/Pay/course2_tmpl&lang=zh_CN&token=25857919#4
	/// </summary>
	public static class TenPayV3 {
		/// <summary>
		/// 统一支付接口，可接受JSAPI/NATIVE/APP 下预支付订单，返回预支付订单号。NATIVE 支付返回二维码code_url。
		/// </summary>
		/// <param name="payment_query"></param>
		/// <param name="openid"></param>
		/// <param name="body">商品名称</param>
		/// <param name="pay_no"></param>
		/// <param name="total_fee"></param>
		/// <param name="notify_url"></param>
		/// <param name="trade_type"></param>
		/// <returns></returns>
		public static async Task<unifiedorder> Unifiedorder(payment_query payment_query, string openid, string body, string pay_no, string total_fee,
			string notify_url, string trade_type = "JSAPI") {
			unifiedorder unifiedorder = new unifiedorder();
			//创建支付应答对象
			RequestHandler packageReqHandler = new RequestHandler(null);
			//初始化
			packageReqHandler.Init();

			unifiedorder.appid = payment_query.app_id;
			unifiedorder.timestamp = TenPayV3Util.GetTimestamp();
			unifiedorder.noncestr = TenPayV3Util.GetNoncestr();

			//设置package订单参数
			packageReqHandler.SetParameter("appid", payment_query.app_id);//公众账号ID
			packageReqHandler.SetParameter("mch_id", payment_query.mchid);//商户号
			packageReqHandler.SetParameter("nonce_str", unifiedorder.noncestr);//随机字符串
			packageReqHandler.SetParameter("body", body);//商品信息
			//packageReqHandler.SetParameter("attach", payment_query.attach);//商品信息
			packageReqHandler.SetParameter("out_trade_no", pay_no);//商家订单号
			packageReqHandler.SetParameter("total_fee", total_fee);//商品金额,以分为单位(money * 100).ToString()
			packageReqHandler.SetParameter("spbill_create_ip", HttpContext.Current.Request.UserHostAddress);//用户的公网ip，不是商户服务器IP
			packageReqHandler.SetParameter("notify_url", notify_url);//接收财付通通知的URL
			packageReqHandler.SetParameter("trade_type", trade_type);//交易类型
			//packageReqHandler.SetParameter("openid", openid);//用户的openId

			string sign = packageReqHandler.CreateMd5Sign("key", payment_query.key);
			packageReqHandler.SetParameter("sign", sign);//签名
			string result = string.Empty;
			var log = IocHelper.Get<ILogHelper>("info");
			try {
				string data = packageReqHandler.ParseXML();
				log.Info(data);
				var urlFormat = "https://api.mch.weixin.qq.com/pay/unifiedorder";
				var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);

				//result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
				var handler = new WebRequestHandler();
				HttpClient client = new HttpClient(handler);
				var content = new ByteArrayContent(formDataBytes);
				result = await client.PostAsync(urlFormat, content).ReceiveString();
				log.Info(result);
				var xml = XDocument.Parse(result).Element("xml");
				string return_code = xml.Element("return_code").Value;
				string return_msg = xml.Element("return_msg").Value;
				if (return_code == "SUCCESS") {
					string result_code = xml.Element("result_code").Value;
					if (result_code == "SUCCESS") {
						string prepayId = xml.Element("prepay_id").Value;
						unifiedorder.prepayid = prepayId;
						//设置支付参数
						if (trade_type == "JSAPI") {
							RequestHandler paySignReqHandler = new RequestHandler(null);
							paySignReqHandler.SetParameter("appId", payment_query.app_id);
							paySignReqHandler.SetParameter("timeStamp", unifiedorder.timestamp);
							paySignReqHandler.SetParameter("nonceStr", unifiedorder.noncestr);
							paySignReqHandler.SetParameter("package", string.Format("prepay_id={0}", prepayId));
							paySignReqHandler.SetParameter("signType", "MD5");
							unifiedorder.paysign = paySignReqHandler.CreateMd5Sign("key", payment_query.key);
							unifiedorder.package = string.Format("prepay_id={0}", prepayId);
						} else if (trade_type == "APP") {
							RequestHandler paySignReqHandler = new RequestHandler(null);
							paySignReqHandler.SetParameter("appid", payment_query.app_id);
							paySignReqHandler.SetParameter("prepayid", unifiedorder.prepayid);
							paySignReqHandler.SetParameter("timestamp", unifiedorder.timestamp);
							paySignReqHandler.SetParameter("noncestr", unifiedorder.noncestr);
							paySignReqHandler.SetParameter("package", "Sign=WXPay");
							paySignReqHandler.SetParameter("partnerid", payment_query.mchid);
							unifiedorder.package = "Sign=WXPay";
							unifiedorder.paysign = paySignReqHandler.CreateMd5Sign("key", payment_query.key);
							unifiedorder.partnerid = payment_query.mchid;
						} else {
							if (xml.Element("code_url") != null)
								unifiedorder.code_url = xml.Element("code_url").Value;
						}
					} else {
						log.Info(result);
						return null;
					}
				} else {
					log.Info(result);
					return null;
				}
			} catch (Exception ex) {
				log.Info(ex.Message);
				return null;
			}
			return unifiedorder;
		}

		/// <summary>
		/// H5支付接口（和“统一支付接口”近似）
		/// </summary>
		/// <param name="dataInfo">微信支付需要post的Data数据，TradeType将会强制赋值为TenPayV3Type.MWEB</param>
		/// <param name="timeOut"></param>
		/// <returns></returns>
		public static UnifiedorderResult Html5Order(TenPayV3UnifiedorderRequestData dataInfo, int timeOut = 10000) {
			var urlFormat = "https://api.mch.weixin.qq.com/pay/unifiedorder";
			dataInfo.TradeType = TenPayV3Type.MWEB;
			var data = dataInfo.PackageRequestHandler.ParseXML();//获取XML
																 //throw new Exception(data.HtmlEncode());
			MemoryStream ms = new MemoryStream();
			var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
			ms.Write(formDataBytes, 0, formDataBytes.Length);
			ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置

			var resultXml = RequestUtility.HttpPost(urlFormat, null, ms, timeOut: timeOut);
			return new UnifiedorderResult(resultXml);
		}

		/// <summary>
		/// 静态的支付二维码
		/// </summary>
		/// <param name="appId">开放平台账户的唯一标识</param>
		/// <param name="timesTamp">时间戳</param>
		/// <param name="mchId">商户Id</param>
		/// <param name="nonceStr">32 位内的随机串，防重发</param>
		/// <param name="productId">商品唯一id</param>
		/// <param name="sign">签名</param>
		public static string NativePay(string appId, string timesTamp, string mchId, string nonceStr, string productId, string sign) {
			var urlFormat = "weixin://wxpay/bizpayurl?sign={0}&appid={1}&mch_id={2}&product_id={3}&time_stamp={4}&nonce_str={5}";
			var url = string.Format(urlFormat, sign, appId, mchId, productId, timesTamp, nonceStr);

			return url;
		}

		/// <summary>
		/// 订单查询接口
		/// </summary>
		/// <param name="payment_query"></param>
		/// <param name="transaction_id"></param>
		/// <param name="out_trade_no"></param>
		/// <returns></returns>
		public static async Task<order_query_model> OrderQuery(payment_query payment_query, string transaction_id = "", string out_trade_no = "") {
			var urlFormat = "https://api.mch.weixin.qq.com/pay/orderquery";
			string nonceStr = TenPayV3Util.GetNoncestr();
			RequestHandler packageReqHandler = new RequestHandler(null);

			order_query_model order_query_model = new order_query_model();
			order_query_model.appid = payment_query.app_id;
			//设置package订单参数
			packageReqHandler.SetParameter("appid", payment_query.app_id);//公众账号ID
			packageReqHandler.SetParameter("mch_id", payment_query.mchid);//商户号
			if (!string.IsNullOrEmpty(transaction_id))
				packageReqHandler.SetParameter("transaction_id", transaction_id);//填入微信订单号 
			if (!string.IsNullOrEmpty(out_trade_no))
				packageReqHandler.SetParameter("out_trade_no", out_trade_no);//填入商家订单号
			packageReqHandler.SetParameter("nonce_str", nonceStr);//随机字符串
			string sign = packageReqHandler.CreateMd5Sign("key", payment_query.key);
			packageReqHandler.SetParameter("sign", sign);//签名
			try {
				string data = packageReqHandler.ParseXML();
				var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
				//var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
				var handler = new WebRequestHandler();
				HttpClient client = new HttpClient(handler);
				var content = new ByteArrayContent(formDataBytes);
				var result = await client.PostAsync(urlFormat, content).ReceiveString();
				var xml = XDocument.Parse(result).Element("xml");
				string return_code = xml.Element("return_code").Value;
				string return_msg = xml.Element("return_msg").Value;
				if (return_code == "SUCCESS" && return_msg == "SUCCESS") {
					order_query_model.openid = xml.Element("openid").Value;
					order_query_model.trade_type = xml.Element("trade_type").Value;
					order_query_model.trade_state = xml.Element("trade_state").Value;
					order_query_model.total_fee = int.Parse(xml.Element("total_fee").Value) / 100;
					order_query_model.transaction_id = xml.Element("transaction_id").Value;
					order_query_model.out_trade_no = xml.Element("out_trade_no").Value;
					var time_end = xml.Element("time_end").Value;
					DateTime pay_time = new DateTime(int.Parse(time_end.Substring(0, 4)), int.Parse(time_end.Substring(4, 2)), int.Parse(time_end.Substring(6, 2)),
						int.Parse(time_end.Substring(8, 2)), int.Parse(time_end.Substring(10, 2)), int.Parse(time_end.Substring(12, 2)));
					order_query_model.transaction_end = pay_time;
				} else {
					order_query_model.return_msg = return_msg;
				}
			} catch {
				order_query_model.return_msg = "可能超时啦，要重新提交";
			}
			return order_query_model;
		}

		/// <summary>
		/// 关闭订单接口
		/// </summary>
		/// <param name="payment_query"></param>
		/// <param name="out_trade_no"></param>
		/// <returns></returns>
		public static async Task<string> CloseOrder(payment_query payment_query, string out_trade_no) {
			var urlFormat = "https://api.mch.weixin.qq.com/pay/closeorder";
			string nonceStr = TenPayV3Util.GetNoncestr();
			RequestHandler packageReqHandler = new RequestHandler(null);

			//设置package订单参数
			packageReqHandler.SetParameter("appid", payment_query.app_id);//公众账号ID
			packageReqHandler.SetParameter("mch_id", payment_query.mchid);//商户号
			packageReqHandler.SetParameter("out_trade_no", out_trade_no);//填入商家订单号
			packageReqHandler.SetParameter("nonce_str", nonceStr);//随机字符串
			string sign = packageReqHandler.CreateMd5Sign("key", payment_query.key);
			packageReqHandler.SetParameter("sign", sign);//签名
			try {
				string data = packageReqHandler.ParseXML();
				var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
				//var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
				var handler = new WebRequestHandler();
				HttpClient client = new HttpClient(handler);
				var content = new ByteArrayContent(formDataBytes);
				var result = await client.PostAsync(urlFormat, content).ReceiveString();
				var xml = XDocument.Parse(result).Element("xml");
				string return_code = xml.Element("return_code").Value;
				string return_msg = xml.Element("return_msg").Value;
				if (return_code == "SUCCESS") {
					return return_code;
				} else {
					throw new ServerErrorException(return_msg, 1002);
				}
			} catch {
				throw new ServerErrorException("可能超时啦，要重新提交", 1002);
			}
		}
		private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) {
			if (errors == SslPolicyErrors.None)
				return true;
			return false;
		}

		/// <summary>
		/// 退款申请接口
		/// </summary>
		/// <returns></returns>
		public static async Task<refund_response> Refund(payment_query payment_query, string transaction_id, string out_trade_no,
												string out_refund_no, decimal total_fee, decimal refund_fee) {
			var urlFormat = "https://api.mch.weixin.qq.com/secapi/pay/refund";

			string nonceStr = TenPayV3Util.GetNoncestr();
			RequestHandler packageReqHandler = new RequestHandler(null);

			//设置package订单参数
			packageReqHandler.SetParameter("appid", payment_query.app_id);       //公众账号ID
			packageReqHandler.SetParameter("mch_id", payment_query.mchid);       //商户号
			if (!string.IsNullOrEmpty(transaction_id))
				packageReqHandler.SetParameter("transaction_id", transaction_id);//微信订单号
			if (!string.IsNullOrEmpty(out_trade_no))
				packageReqHandler.SetParameter("out_trade_no", out_trade_no);//填入商家订单号
			packageReqHandler.SetParameter("out_refund_no", out_refund_no);//填入退款订单号
			packageReqHandler.SetParameter("total_fee", Convert.ToInt32(total_fee * 100).ToString());               //填入总金额
			packageReqHandler.SetParameter("refund_fee", Convert.ToInt32(refund_fee * 100).ToString());               //填入退款金额
			packageReqHandler.SetParameter("op_user_id", payment_query.mchid);   //操作员Id，默认就是商户号
			packageReqHandler.SetParameter("nonce_str", nonceStr);              //随机字符串
			string sign = packageReqHandler.CreateMd5Sign("key", payment_query.key);
			packageReqHandler.SetParameter("sign", sign);//签名
														 //退款需要post的数据
			try {
				string data = packageReqHandler.ParseXML();
				var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
				//本地或者服务器的证书位置（证书在微信支付申请成功发来的通知邮件中）
				string cert = HttpContext.Current.Server.MapPath("~/content/cert/apiclient_cert.p12");
				//私钥（在安装证书时设置）
				ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
				//调用证书
				X509Certificate2 cer = new X509Certificate2(cert, "", X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);

				var handler = new WebRequestHandler();
				handler.ClientCertificates.Add(cer);
				HttpClient client = new HttpClient(handler);
				var content = new ByteArrayContent(formDataBytes);
				var result = await client.PostAsync(urlFormat, content).ReceiveString();
				//var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
				var xml = XDocument.Parse(result).Element("xml");
				string return_code = xml.Element("return_code").Value;
				string return_msg = xml.Element("return_msg").Value;
				string refund_id = xml.Element("refund_id").Value;
				if (return_code == "SUCCESS") {
					return new refund_response { code = "10000", msg = "", trade_no = refund_id };
				} else {
					return new refund_response { code = "1002", msg = return_msg };
					//throw new ServerErrorException(return_msg, 1002);
				}
			} catch {
				return new refund_response { code = "1002", msg = "可能超时啦，要重新提交" };
				//throw new ServerErrorException("可能超时啦，要重新提交", 1002);
			}
		}

		/// <summary>
		/// 退款查询接口
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static async Task<string> RefundQuery(payment_query payment_query, string transaction_id = "", string out_trade_no = "",
			string out_refund_no = "", string refund_id = "") {
			var urlFormat = "https://api.mch.weixin.qq.com/pay/refundquery";
			string nonceStr = TenPayV3Util.GetNoncestr();
			RequestHandler packageReqHandler = new RequestHandler(null);

			//设置package订单参数
			packageReqHandler.SetParameter("appid", payment_query.app_id);       //公众账号ID
			packageReqHandler.SetParameter("mch_id", payment_query.mchid);       //商户号
			if (!string.IsNullOrEmpty(transaction_id))
				packageReqHandler.SetParameter("transaction_id", transaction_id);//原订单的微信订单号
			if (!string.IsNullOrEmpty(out_trade_no))
				packageReqHandler.SetParameter("out_trade_no", out_trade_no);//原来的订单号	
			if (!string.IsNullOrEmpty(out_refund_no))
				packageReqHandler.SetParameter("out_refund_no", out_refund_no);//商户退款单号
			if (!string.IsNullOrEmpty(refund_id))
				packageReqHandler.SetParameter("refund_id", refund_id);   //微信退款单号
			packageReqHandler.SetParameter("nonce_str", nonceStr);              //随机字符串
			string sign = packageReqHandler.CreateMd5Sign("key", payment_query.key);
			packageReqHandler.SetParameter("sign", sign);//签名
														 //退款需要post的数据
			try {
				string data = packageReqHandler.ParseXML();
				var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
				//var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
				var handler = new WebRequestHandler();
				HttpClient client = new HttpClient(handler);
				var content = new ByteArrayContent(formDataBytes);
				var result = await client.PostAsync(urlFormat, content).ReceiveString();
				var xml = XDocument.Parse(result).Element("xml");
				string return_code = xml.Element("return_code").Value;
				string return_msg = xml.Element("return_msg").Value;
				if (return_code == "SUCCESS") {
					return return_code;
				} else {
					throw new ServerErrorException(return_msg, 1002);
				}
			} catch {
				throw new ServerErrorException("可能超时啦，要重新提交", 1002);
			}
		}

		/// <summary>
		/// 对账单接口
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static async Task<string> DownloadBill(string data) {
			var urlFormat = "https://api.mch.weixin.qq.com/pay/downloadbill";

			var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
			var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
			return result;
		}

		/// <summary>
		/// 短链接转换接口
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static async Task<string> ShortUrl(string data) {
			var urlFormat = "https://api.mch.weixin.qq.com/tools/shorturl";

			var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
			var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
			return result;
		}

		/// <summary>
		/// 刷卡支付
		/// 提交被扫支付
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static async Task<string> MicroPay(string data) {
			var urlFormat = "https://api.mch.weixin.qq.com/pay/micropay";

			var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
			var result = await urlFormat.PostUrlEncodedAsync(formDataBytes).ReceiveString();
			return result;
		}
	}
}
